package com.cupdata.zicon.thrift.client.pool;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.pool2.BaseKeyedPooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.thrift.TServiceClient;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TMultiplexedProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.cupdata.zicon.thrift.config.ThriftClientAutoConfiguration;
import com.netflix.appinfo.EurekaInstanceConfig;
import com.netflix.discovery.EurekaClient;


public class ThriftClientPooledObjectFactory extends BaseKeyedPooledObjectFactory<ThriftClientKey, TServiceClient> {
	
	public static final int DEFAULT_CONNECTION_TIMEOUT = 1000;
    public static final int DEFAULT_READ_TIMEOUT = 30000;
    public static final int DEFAULT_MAX_RETRIES = 1;
    
    private static Map<String, List<ServiceInstance>> serviceMap = new HashMap<>();
    
	private static DiscoveryClient discoveryClient;
	
	private TSocket tsocket;
	
//	private static int rebuildTimes = 0;
	
	private static boolean toCreate = false;
	
//	@Bean
//	public DiscoveryClient discoveryClient(EurekaInstanceConfig config,
//			EurekaClient client) {
//		return new EurekaDiscoveryClient(config, client);
//	}
    
	@Override
	
	public TServiceClient create(ThriftClientKey clientKey) throws Exception {
		System.out.println("Pool Object Factory create...");
		List<ServiceInstance> serviceList = serviceMap.get(clientKey.getServiceId());
		if(serviceList == null){
			return null;
		}
		Collections.shuffle(serviceList);
		ServiceInstance serviceInstance = serviceList.get(0);
		String ip = serviceInstance.getHost();
		String port = serviceInstance.getMetadata().get("port");
		if(port == null){
			throw new Exception("Please config service " + clientKey.getServiceName() + " port");
		}
	    tsocket = new TSocket(ip,Integer.valueOf(port));
//		tSocket.setTimeout(0);
//		tSocket.close();
		try {
			tsocket.open();
		} catch (TTransportException e) {
			e.printStackTrace();
		}
		
		TMultiplexedProtocol protocol = new TMultiplexedProtocol(new TBinaryProtocol(tsocket), clientKey.getServiceName());
		
		return BeanUtils.instantiateClass(clientKey.getClazz().getConstructor(TProtocol.class), (TProtocol)protocol);
	}
	
	
	@Override
	public PooledObject<TServiceClient> wrap(TServiceClient client) {
		 return new ThriftClientPooledObject<>(client);
	}
	
	/* (non-Javadoc)
	 * @see org.apache.commons.pool2.BaseKeyedPooledObjectFactory#makeObject(java.lang.Object)
	 * Create an instance that can be served by the pool and wrap it in a PooledObject to be managed by the pool.
	 */
	@Override
	public PooledObject<TServiceClient> makeObject(ThriftClientKey key)
			throws Exception {
		System.out.println("makeObject...");
		return super.makeObject(key);
	}


	/* (non-Javadoc)
	 * @see org.apache.commons.pool2.BaseKeyedPooledObjectFactory#destroyObject(java.lang.Object, org.apache.commons.pool2.PooledObject)
	 */
	@Override
	public void destroyObject(ThriftClientKey key,
			PooledObject<TServiceClient> p) throws Exception {
		//关闭Socket连接
//		tSocket.close();
//		if(++rebuildTimes>=3) {
		//如果重连3次失败，重新更新ServiceMap列表
		String serviceId = "cogate-thrift-server";
		List<ServiceInstance> intances = discoveryClient.getInstances(serviceId);
		serviceMap.put(serviceId, intances);
//		ThriftClientAutoConfiguration configuration = new ThriftClientAutoConfiguration();
//		configuration.setDiscoveryClient();
//		rebuildTimes = 0;	
//		transport.flush();
		tsocket.close();
//		transport = null;
		//重新建立连接
	    create(key);
//		}
		System.out.println("destroyObject...");
	}


	/* (non-Javadoc)
	 * @see org.apache.commons.pool2.BaseKeyedPooledObjectFactory#validateObject(java.lang.Object, org.apache.commons.pool2.PooledObject)
	 */
	/*@Override
	public boolean validateObject(ThriftClientKey key,
			PooledObject<TServiceClient> p) {
//		discoveryClient = null;
//		List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances(key.getServiceId());
//		ServiceInstance instance = discoveryClient.getLocalServiceInstance();
		if (!tSocket.isOpen()) {
			isDisconnected = true;
		} 
		System.out.println("validateObject...");
		return isDisconnected;
	}*/


	/* (non-Javadoc)
	 * @see org.apache.commons.pool2.BaseKeyedPooledObjectFactory#activateObject(java.lang.Object, org.apache.commons.pool2.PooledObject)
	 */
	@Override
	public void activateObject(ThriftClientKey key,
		PooledObject<TServiceClient> p) throws Exception {
		System.out.println("activateObject...");
		if(toCreate) {
			destroyObject(key, p);
		}
		
	}


	/* (non-Javadoc)
	 * @see org.apache.commons.pool2.BaseKeyedPooledObjectFactory#passivateObject(java.lang.Object, org.apache.commons.pool2.PooledObject)
	 */
	/*@Override
	public void passivateObject(ThriftClientKey key,
			PooledObject<TServiceClient> p) throws Exception {
		if (!validateObject(key,p)) {
			destroyObject(key,p);
		}
		System.out.println("passivateObject...");
	}*/


	public static Map<String, List<ServiceInstance>> getServiceMap() {
		return serviceMap;
	}
	public static void setServiceMap(Map<String, List<ServiceInstance>> serviceMap) {
		ThriftClientPooledObjectFactory.serviceMap = serviceMap;
	}


	/**
	 * @return the discoveryClient
	 */
	public static DiscoveryClient getDiscoveryClient() {
		return discoveryClient;
	}

	/**
	 * @param discoveryClient the discoveryClient to set
	 */
	public static void setDiscoveryClient(DiscoveryClient discoveryClient) {
		ThriftClientPooledObjectFactory.discoveryClient = discoveryClient;
	}


	public static boolean isToCreate() {
		return toCreate;
	}


	public static void setToCreate(boolean toCreate) {
		ThriftClientPooledObjectFactory.toCreate = toCreate;
	}
	
	/**
	 * @param rebuildTimes the rebuildTimes to set
	 */
	/*public static void addRebuildTimes() {
		rebuildTimes++;
	}*/
	
}
